import re
import datetime
import urllib
import urlparse

import httplib2
from BeautifulSoup import BeautifulSoup
from sqlobject import *

import parser
from model import *


def retrieve(buoy, variable, start, end, frequency):
    # Extract lat/lon.
    latitude, n, longitude, e = re.match(r'(\d+)(\w)(\d+)(\w)', buoy).groups()
    latitude = float(latitude)
    longitude = float(longitude)
    if n == 's': latitude *= -1.0
    if e == 'w': longitude *= -1.0
    
    # Create table if necessary.
    Sample.createTable(ifNotExists=True)

    # Call appropriate function.
    {'t'   : _get_subsurface_temp,
     's'   : _get_salinity,
     'd'   : _get_density,
     'rad' : _get_shortwave_radiation,
     'rain': _get_precipitation,
     'iso' : _get_isotherm_depth,
     'met' : _get_meteorology,
    }[variable](buoy, latitude, longitude, start, end, frequency)


def _get_subsurface_temp(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    Temperature.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = Temperature.select(Temperature.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in Temperature.select(AND(Temperature.q.datetime>=start, Temperature.q.datetime<=end, Temperature.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 't', start, end, frequency)
        if data:
            data = parser._subsurface_temp(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                Temperature(**value)

                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.temperature = value['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude, temperature=value['value'])


def _get_salinity(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    Salinity.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = Salinity.select(Salinity.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in Salinity.select(AND(Salinity.q.datetime>=start, Salinity.q.datetime<=end, Salinity.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 's', start, end, frequency)
        if data:
            data = parser._salinity(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                Salinity(**value)

                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.salinity = value['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude, salinity=value['value'])


def _get_density(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    Density.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = Density.select(Density.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in Density.select(AND(Density.q.datetime>=start, Density.q.datetime<=end, Density.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 'd', start, end, frequency)
        if data:
            data = parser._density(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                Density(**value)

                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.density = value['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude, density=value['value'])


def _get_shortwave_radiation(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    ShortwaveRadiation.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = ShortwaveRadiation.select(ShortwaveRadiation.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in ShortwaveRadiation.select(AND(ShortwaveRadiation.q.datetime>=start, ShortwaveRadiation.q.datetime<=end, ShortwaveRadiation.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 'rad', start, end, frequency)
        if data:
            data = parser._shortwave_radiation(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                ShortwaveRadiation(**value)

                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.shortwaveradiation = value['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude, shortwaveradiation =value['value'])


def _get_precipitation(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    Precipitation.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = Precipitation.select(Precipitation.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in Precipitation.select(AND(Precipitation.q.datetime>=start, Precipitation.q.datetime<=end, Precipitation.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 'rain', start, end, frequency)
        if data:
            data = parser._precipitation(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                Precipitation(**value)

                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.precipitation = value['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude, precipitation=value['value'])


def _get_isotherm_depth(buoy, latitude, longitude, start, end, frequency):
    # Create table if necessary.
    IsothermDepth.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = IsothermDepth.select(IsothermDepth.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)
        
    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in IsothermDepth.select(AND(IsothermDepth.q.datetime>=start, IsothermDepth.q.datetime<=end, IsothermDepth.q.buoy==buoy))] 
        
        # Fetch and parse data.
        data = fetch(buoy, 'iso', start, end, frequency)
        if data:
            data = parser._isotherm_depth(data)

            # Store data.
            for value in data:
                value['buoy'] = buoy
                IsothermDepth(**value)


def _get_meteorology(buoy, latitude, longitude, start, end, frequency):
    # Create tables if necessary.
    UWind.createTable(ifNotExists=True)
    VWind.createTable(ifNotExists=True)
    WindSpeed.createTable(ifNotExists=True)
    WindDirection.createTable(ifNotExists=True)
    AirTemperature.createTable(ifNotExists=True)
    RelativeHumidity.createTable(ifNotExists=True)

    if start is None:
        # Select last date.
        sel = RelativeHumidity.select(RelativeHumidity.q.buoy==buoy, orderBy='-datetime')
        if sel.count():
            start = sel[0].datetime
            # Convert to datetime.date to compare with end.
            start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=1)
        else:
            # Get everything.
            start = datetime.date(1997, 3, 1)

    if end>=start:
        # Delete old data.
        deleted = [row.destroySelf() for row in UWind.select(AND(UWind.q.datetime>=start, UWind.q.datetime<=end, UWind.q.buoy==buoy))]
        deleted = [row.destroySelf() for row in VWind.select(AND(VWind.q.datetime>=start, VWind.q.datetime<=end, VWind.q.buoy==buoy))]
        deleted = [row.destroySelf() for row in WindSpeed.select(AND(WindSpeed.q.datetime>=start, WindSpeed.q.datetime<=end, WindSpeed.q.buoy==buoy))]
        deleted = [row.destroySelf() for row in WindDirection.select(AND(WindDirection.q.datetime>=start, WindDirection.q.datetime<=end, WindDirection.q.buoy==buoy))]
        deleted = [row.destroySelf() for row in AirTemperature.select(AND(AirTemperature.q.datetime>=start, AirTemperature.q.datetime<=end, AirTemperature.q.buoy==buoy))]
        deleted = [row.destroySelf() for row in RelativeHumidity.select(AND(RelativeHumidity.q.datetime>=start, RelativeHumidity.q.datetime<=end, RelativeHumidity.q.buoy==buoy))]

        # Fetch and parse data.
        data = fetch(buoy, 'met', start, end, frequency)
        if data:
            data = parser._meteorology(data)

            # Store data.
            for values in data:
                for value in values: value['buoy'] = buoy
                UWind(**values[0])
                VWind(**values[1])
                WindSpeed(**values[2])
                WindDirection(**values[3])
                AirTemperature(**values[4])
                RelativeHumidity(**values[5])

                value = values[0]  # get any value, just for date and depth
                if value['depth'] < 1: value['depth'] = 1
                sel = Sample.select(AND(Sample.q.latitude==latitude, Sample.q.longitude==longitude, 
                                        Sample.q.datetime==value['datetime'], 
                                        Sample.q.depth==value['depth']))
                if sel.count():
                    sample = sel[0]
                    sample.uwind = values[0]['value']
                    sample.vwind = values[1]['value']
                    sample.windspeed = values[2]['value']
                    sample.winddirection = values[3]['value']
                    sample.airtemperature = values[4]['value']
                    sample.relativehumidity = values[5]['value']
                else:
                    Sample(datetime=value['datetime'], depth=value['depth'], latitude=latitude, longitude=longitude,
                           uwind=values[0]['value'],
                           vwind=values[1]['value'],
                           windspeed=values[2]['value'],
                           winddirection=values[3]['value'],
                           airtemperature=values[4]['value'],
                           relativehumidity=values[5]['value'])


def fetch(buoy, variable, start=None, end=None, frequency='dy'):
    if start is None: start = datetime.date(1997, 3, 1)
    if end is None: end = datetime.date.today()

    url = "http://www.pmel.noaa.gov/cgi-tao/cover.cgi"

    params = {'P1' : 'deliv',           # Delivery Mode: deliv or avail
              'P2' : variable,          # variable: lw, rad, rain, met, wind, rh, airt, sst, t, cur, adcp, s, d, bp, p, dyn, iso
              'P3' : frequency,         # frequency: dy, hr, 10m, hres
              'P4' : start.year,        # iys start year YYYY (use Year  for default)
              'P5' : start.month,       # ims start month MM  (use Month for default)
              'P6' : start.day,         # ids start day   DD  (use Day for default)
              'P7' : end.year,          # iye end   year YYYY (use Year  for default)
              'P8' : end.month,         # ime end   month MM  (use Month for default)
              'P9' : end.day,           # ide end   month DD  (use Day for default)
              'P10': 'buoy',            # file structure: buoy, buoytar, one
              'P11': 'ascii',           # format: ascii, cdf4, cdf2
              'P12': 'None',            # icomp Z, zip, or None
              'P13': 'pir',             # array: tt, pir, active, all, or io
              'P14': '',                # user
              'P15': '',                # organization
              'P16': '',                # email
              'P17': '',                # description
              'P18': 322,               # minlon
              'P19': 360,               # maxlon
              'P20': -19,               # minlat
              'P21': 15,                # maxlat
              'P22': 'html',            # software source: java or html
             }
    params['script'] = 'deliv/nojava.csh'

    checkbox = {
                '15n38w': 'p25',
                '12n38w': 'p26',
                '8n38w' : 'p27',
                '4n38w' : 'p28',
                '2n10w' : 'p29',
                '0n35w' : 'p30',
                '0n23w' : 'p31',
                '0n10w' : 'p32',
                '0n0e'  : 'p33',
                '2s10w' : 'p34',
                '6s10w' : 'p35',
                '8s30w' : 'p36',
                '10s10w': 'p37',
                '14s32w': 'p38',
                '19s34w': 'p39',
               }[buoy]
    params[checkbox] = buoy
    
    params = urllib.urlencode(params)
    url = "%s?%s" % (url, params)
    print 'Downloading from %s' % url

    h = httplib2.Http(".cache")
    resp, content = h.request(url, "GET")

    # Grab link from content.
    soup = BeautifulSoup(content)
    link = soup.findAll('a', href=re.compile('.ascii'))
    if link:
        #url = link[0]['href'].strip()
        url = urlparse.urljoin(url, link[0]['href'].strip())
        print 'Downloading data from link %s' % url
        resp, content = h.request(url, "GET")

        return content

